package com.gitee.wsl.transform.encode.base

import com.gitee.wsl.ext.base.BYTE_BITS
import com.gitee.wsl.ext.array.toBinaryString
import com.gitee.wsl.ext.string.padding
import com.gitee.wsl.ext.string.toByteArray
import com.gitee.wsl.transform.encode.ByteArrayEncode
import com.gitee.wsl.transform.encode.ByteArrayEncodeDecode
import com.gitee.wsl.transform.encode.DecodeToByteArray
import com.gitee.wsl.transform.encode.DecodeToString
import com.gitee.wsl.transform.encode.StringEncode
import com.gitee.wsl.transform.encode.StringToStringEncodeDecode

const val BASE32_DICT = "ABCDEFGHIJKLMNOPQRSTUVWXYZ234567"
const val BASE32_BLOCK_SIZE = 5

internal object Base32: StringToStringEncodeDecode {
    override fun encode(src: String): String = src.encodeBase32ToString()

    override fun decode(src: String): String = src.decodeBase32().decodeToString()

}

val StringEncode.Companion.base32:StringEncode<String> get() = Base32

val DecodeToString.Companion.base32:DecodeToString<String> get() = Base32

fun String.encodeBase32ToString(dict: String = BASE32_DICT) = toByteArray().encodeBase32(dict)

internal object ByteArrayBase32: ByteArrayEncodeDecode<String> {
    override fun encode(src: ByteArray): String = src.encodeBase32()

    override fun decode(src: String): ByteArray = src.decodeBase32()

}

val ByteArrayEncode.Companion.base32 : ByteArrayEncode<String> get() = ByteArrayBase32

val DecodeToByteArray.Companion.base32 :DecodeToByteArray<String> get() = ByteArrayBase32

fun ByteArray.encodeBase32(dict: String = BASE32_DICT) =
    toBinaryString()
        .chunked(BASE32_BLOCK_SIZE)
        .joinToString("") {
            dict.ifEmpty { BASE32_DICT }[it.padding("0", BASE32_BLOCK_SIZE).toInt(2)].toString()
        }
        .padding("=", BYTE_BITS) // lcm (5,8) /5 = 8

fun String.decodeBase32(dict: String = BASE32_DICT) =
    asIterable()
        .filter { it != '=' }
        .joinToString("") {
            dict
                .ifEmpty { BASE32_DICT }
                .indexOf(it)
                .toString(2)
                .padding("0", BASE32_BLOCK_SIZE, false)
        }
        .chunked(BYTE_BITS)
        .map { it.toInt(2).toByte() }
        .filter { it.toInt() != 0 }
        .toByteArray()



